#include <tinx/memory.h>
#include <tinx/cpu.h>
#include <tinx/io.h>
#include <tinx/apic.h>
#include <tinx/acpi.h>
#include <tinx/interrupt.h>
#include <tinx/printk.h>
#include <tinx/list.h>

static ACPI_MADT *madt;
static u8 *lapic;

#define APIC_REG_PTR(reg) ((u32 *)(lapic + reg))
#define APIC_REG(reg) (*APIC_REG_PTR(reg))

void apic_send_eoi(u8 irq)
{
    APIC_REG(APIC_REG_EOI) = 0x0;
}

extern void pic_disable();

void local_apic_init()
{
    // 关闭 PIC
    pic_disable();

    // 开启 APIC
    asm volatile(
        "movl %0, %%ecx\n"
        "rdmsr\n"
        "orl %1, %%eax\n"
        "wrmsr\n"
        ::"i"(IA32_MSR_APIC_BASE), "i"(IA32_APIC_ENABLE)
        : "eax", "ecx", "edx");

    // 获取 apic 物理地址
    lapic = (u8 *)madt->local_apic_address;
    map_area((u32)lapic, PAGE_SIZE);

    APIC_REG(APIC_REG_LVT_TIMER) = 0x100000;

    APIC_REG(APIC_REG_SVR) = 0x1ff;

    // 接收中断
    APIC_REG(APIC_REG_TPR) = 0;

    // 启用 FLAT 模式
    APIC_REG(APIC_REG_DFR) = 0xffffffff;

    // 设置目标
    APIC_REG(APIC_REG_LDR) &= (0xff << 24);
    APIC_REG(APIC_REG_LDR) |= 1 << APIC_REG(APIC_REG_ID) << 24;

    APIC_REG(APIC_REG_ESR) = 0;

    APIC_REG(APIC_REG_LVT_TIMER) = 0x20;

    
}

bool apic_check()
{
    cpu_version_t ver;
    cpu_version(&ver);

    if (!ver.APIC)
        return false;

    return true;
}

void apic_init()
{
    if (!apic_check())
        return;

    madt = (ACPI_MADT *)acpi_find_table("APIC");
    MADT_EntryHeader *ptr = (MADT_EntryHeader *)((u32)madt + sizeof(ACPI_MADT));

    for (int i = 0; i < madt->header.Length - sizeof(ACPI_MADT); i += ptr->length)
    {
        if (ptr->type == MADT_IO_APIC)
        {
            MADT_IOApic *ioapic = (MADT_IOApic *)ptr;
            printk("Found IO apic at 0x%p\n", ioapic->io_apic_address);
        }
        ptr = (MADT_EntryHeader *)((u32)madt + sizeof(ACPI_MADT) + i);
    }

    local_apic_init();
}